#include	"queue.h"

#define	DFL_ZQUEUE_SIZE			8192
#define	ZQUEUE_MAX_SIZE			256

static		zqueue_t * zqueues[ZQUEUE_MAX_SIZE];
unsigned 	short	   zqueue_id;

static int			_enqueue(zqueue_t *zqueue, void *data, int count);
static int			_dequeue(zqueue_t *zqueue, void *data, int count);
static zqueue_t *	create_zqueue(int size);
static int			zqueue_index(int id);
static zqueue_t * 	get_zqueue(int id);


#define	_zqueue_empty(zqueue) \
	(zqueue->front == zqueue->rear)

#define	_zqueue_full(zqueue)	\
	((zqueue->rear + 1) % zqueue->size == zqueue->front)

#define	_test_enzqueue(zqueue, count)	\
	(count > 0 && count <= enzqueue_space(zqueue))

#define	_test_dezqueue(zqueue, count)	\
	(count > 0 && count <= dezqueue_space(zqueue))

#define	enzqueue_space(zqueue)	\
	((zqueue->rear < zqueue->front) ? zqueue->front - zqueue->rear - 1 : \
									  zqueue->size - (zqueue->rear - zqueue->front) - 1)
									  
#define	dezqueue_space(zqueue)	\
	((zqueue->front <= zqueue->rear) ? zqueue->rear - zqueue->front : \
									   zqueue->size - (zqueue->front - zqueue->rear))
	
zqueue_t *
create_zqueue(int size){
	int 	   index = 0;
	zqueue_t * zqueue = NULL;
	
	do {
		for (index = 0; index < ZQUEUE_MAX_SIZE; index++){
			if (zqueues[index] == NULL)
				break;
		}
		
		if (index == ZQUEUE_MAX_SIZE)
			break;
		
		zqueue = (zqueue_t *)malloc(sizeof(zqueue_t));
		if (zqueue == NULL)
			break;
		
		memset(zqueue, 0, sizeof(zqueue_t));
		
		zqueue->buf = (char *)malloc(size);
		if (zqueue->buf == NULL){
			free(zqueue);
			zqueue = NULL;
			break;
		}
		
		memset(zqueue->buf, 0, size);
		zqueues[index] = zqueue;
	} while(0);

	return zqueue;
}

int
new_zqueue(int size){
	zqueue_t * zqueue = NULL;
	
	if (size <= 0)
		size = DFL_ZQUEUE_SIZE;
		
	if ((zqueue = create_zqueue(size))== NULL)
		return ERR_ZQUEUE_ID;
	
	zqueue->id 		= (int)zqueue_id++;
	zqueue->size	= size;
	
	pthread_mutex_init(&zqueue->q_mutex, NULL);
	pthread_mutex_init(&zqueue->mutex, NULL);
	
	return zqueue->id;
}

int
destroy_zqueue(int id){
	int index = zqueue_index(id);
	
	if (index >= 0)
	{
		pthread_mutex_destroy(&zqueues[index]->q_mutex);
		pthread_mutex_destroy(&zqueues[index]->mutex);
		
		free(zqueues[index]->buf);
		free(zqueues[index]);
		zqueues[index] = NULL;
		
		return 0;
	}
	else
		return -1;
}

int
enzqueue(int id, void *data, int count){
	
	zqueue_t * zqueue = NULL;
	
	if (count <= 0 || (zqueue = get_zqueue(id)) == NULL)
		return -1;

	pthread_mutex_lock(&zqueue->q_mutex);
	count = _enqueue(zqueue, data, count);
	pthread_mutex_unlock(&zqueue->q_mutex);
		
	return count;
}

int
dezqueue(int id, void *data, int count){

	zqueue_t * zqueue = NULL;
	
	if (count <= 0 || (zqueue = get_zqueue(id)) == NULL)
		return -1;
	
	pthread_mutex_lock(&zqueue->q_mutex);
	count = _dequeue(zqueue, data, count);
	pthread_mutex_unlock(&zqueue->q_mutex);
	
	return count;
}

int
_enqueue(zqueue_t *zqueue, void *data, int count){
	
	int		   remain = 0;
	
	if (_test_enzqueue(zqueue, count)){
		if (zqueue->rear + count <= zqueue->size){
			memcpy(&zqueue->buf[zqueue->rear], data, count);
			
			if (zqueue->rear + count == zqueue->size)
				zqueue->rear = 0;
			else
				zqueue->rear += count;
		} else {
			memcpy(&zqueue->buf[zqueue->rear], data, zqueue->size - zqueue->rear);
			remain = (zqueue->rear + count) % zqueue->size;
			memcpy(&zqueue->buf[0], data + (zqueue->size - zqueue->rear), remain);
			zqueue->rear = remain;
		}
	} else {
		count = 0;
	}
	
	return count;
}

int
_dequeue(zqueue_t *zqueue, void *data, int count){
	
	int		   remain = 0;
	
	if (_test_dezqueue(zqueue, count)){
		if (zqueue->front + count <= zqueue->size){
			memcpy(data, &zqueue->buf[zqueue->front], count);
			
			if (zqueue->front + count == zqueue->size)
				zqueue->front = 0;
			else
				zqueue->front += count;
		} else {
			memcpy(data, &zqueue->buf[zqueue->front], zqueue->size - zqueue->front);
			remain = (zqueue->front + count) % zqueue->size;
			memcpy(data + (zqueue->size - zqueue->front), &zqueue->buf[0], remain);
			zqueue->front = remain;
		}
	} else {
		//printf("re-dezqueue:%d\n", dezqueue_space(zqueue));
		count = _dequeue(zqueue, data, dezqueue_space(zqueue));
	}
	
	return count;
}

zqueue_t * 
get_zqueue(int id){
	
	int index = 0;
	
	if (id < 0)
		return NULL;
		
	for (index = 0; index < ZQUEUE_MAX_SIZE; index++){
		if (zqueues[index] != NULL && zqueues[index]->id == id)
			break;
	}
	
	return (index < ZQUEUE_MAX_SIZE ? zqueues[index] : NULL);
}

int
zqueue_index(int id)
{
	int index = 0;
	
	if (id < 0)
		return ERR_ZQUEUE_ID;
		
	for (index = 0; index < ZQUEUE_MAX_SIZE; index++){
		if (zqueues[index] != NULL && zqueues[index]->id == id)
			break;
	}
	
	if (index < ZQUEUE_MAX_SIZE)
		return index;
	else
		return ERR_ZQUEUE_ID;
}

int
test_enzqueue(int id, int count){
	zqueue_t * zqueue = NULL;
	
	if ((zqueue = get_zqueue(id)) == NULL)
		return -1;
	
	return _test_enzqueue(zqueue, count);
}

int
test_dezqueue(int id, int count){
	zqueue_t * zqueue = NULL;
	
	if ((zqueue = get_zqueue(id)) == NULL)
		return -1;
	
	return _test_dezqueue(zqueue, count);
}

int
zqueue_empty(int id){
	zqueue_t * zqueue = NULL;
	
	if ((zqueue = get_zqueue(id)) == NULL)
		return TRUE;
	
	return _zqueue_empty(zqueue);
}

int
zqueue_full(int id){
	zqueue_t * zqueue = NULL;
	
	if ((zqueue = get_zqueue(id)) == NULL)
		return FALSE;
	
	return _zqueue_full(zqueue);
}

int
zqueue_lock(int id){
	zqueue_t * zqueue = NULL;
	
	if ((zqueue = get_zqueue(id)) == NULL)
		return -1;
	
	return pthread_mutex_lock(&zqueue->mutex);
}

int
zqueue_unlock(int id){
	zqueue_t * zqueue = NULL;
	
	if ((zqueue = get_zqueue(id)) == NULL)
		return -1;
	
	return pthread_mutex_unlock(&zqueue->mutex);
}

